查看原文
其他

精通Linux系列十:如何在4个G的日志中找到错误记录(文件文本操作)

拾叁 更AI 2023-10-21

精通Linux系列点击关注公众号,AI&编程干货及时送达   


文件文本操作

命令含义
grep在文件中查找符合正则表达式的行。
cut从文件中提取列。
paste追加列。
tr将字符翻译成其他字符。
expand,``unexpand在制表符和空格之间转换。
sort按各种标准对文本行进行排序。
uniq在文件中定位相同的行。
tee复制文件在标准输出上同时打印它。

Linux最大的优点就是文本操作:我们通过应用变换,将文本文件(或标准输入)调整到所需的形式,这个操作常常是在管道中进行的。任何读取标准输入和写入标准输出的程序都属于这个范畴,这里我们将介绍一些最重要的工具。

grep

stdin  stdout  - file  -- opt  --help  --version

grep [选项] 模式 [文件]

grep(grep) 命令是Linux工具库中最常用、最强大的工具之一。它的基本理念很简单:给定一个或多个文件,打印出所有符合特定正则表达式模式的行。例如,如果一个文件 randomlines(随机行) 包含以下行:

棕色狐狸快速的跳过去了!
我妈妈刚给我们煎了九个煎饼。
电影在十一点。

如果我们搜索包含“煎饼”的所有行,我们得到:

→ grep pancake randomlines
我妈妈刚给我们煎了九个煎饼。

现在我们用一个正则表达式来匹配以感叹号结尾的行:

→ grep '\!$' randomlines
棕色狐狸快速的跳过去了!

grep(grep) 可以使用两种不同类型的正则表达式,称为 basic(基本) 和 extended(扩展)。基本语法在 Table 2 中。学习正则表达式是非常值得的。很多Linux程序也使用它们,比如 sed(sed) 和 perl(perl)。

有用的选项

-v只打印符合正则表达式的行。
-l只打印包含匹配行的文件的名称,而不是行本身。
-L只打印包含匹配行的文件的名称。
-c只打印匹配行的计数。
-n在每一行匹配的输出前面,打印其原始行号。
-b在每一行匹配的输出前面,打印该行在输入文件中的字节偏移。
-i大小写不敏感匹配。
-w只匹配完整的单词(即,与整个正则表达式匹配的单词)。
-x只匹配完整的行(即,与整个正则表达式匹配的行)。覆盖 -w
-A N在每个匹配行后,打印其文件中的下一个 N 行。
-B N在每个匹配行前,打印其文件中的前一个 N 行。
-C N与 -A N -B N 相同:打印每个匹配行上方  下方的 N 行(来自原始文件)。
--color=always用颜色突出显示匹配的文本,以提高可读性。
-r递归搜索目录及其子目录中的所有文件。
-E使用扩展正则表达式。参见 egrep(egrep)。
-F使用固定字符串列表而非正则表达式。参见 fgrep(fgrep)。

egrep

stdin  stdout  - file  -- opt  --help  --version

egrep [选项] 模式 [文件]

egrep(egrep) 命令就像 grep(grep),但是使用不同的(“扩展的”)语言进行正则表达式。它与 grep -E 是相同的。

表达式含义
.任意单一字符。
[...]匹配此列表中的任意单一字符。
[^...]匹配此列表中不存在的任意单一字符。
(...)分组。
|`
^行首。
$行尾。
\<单词的开始。
\>单词的结束。
[:alnum:]任何字母数字字符。
[:alpha:]任何字母字符。
[:cntrl:]任何控制字符。
[:digit:]任何数字。
[:graph:]任何图形字符。
[:lower:]任何小写字母。
[:print:]任何可打印字符。
[:punct:]任何标点符号。
[:space:]任何空格字符。
[:upper:]任何大写字母。
[:xdigit:]任何十六进制数字。
*一个或多个重复的正则表达式。
\++ 正则表达式重复一次或多次。
\?? 正则表达式出现零次或一次。
\{n \}{n } 正则表达式重复*n*次。
\{ n ,\}{n ,} 正则表达式重复*n*次或更多次。
\{ n , m \}{ n , m } 正则表达式重复次数在*nm(含)之间,n< *m*。
\c字符*c的字面值,即使c是特殊的正则表达式字符。例如,使用 * 来匹配星号或者 \ 来匹配反斜杠。或者,将字面字符放在方括号中,比如 [] 或 []。

GREP 和行结束字符

当你使用 grep(grep) 匹配行尾($)时,如果文本文件是在Microsoft Windows或Mac OS X系统上创建的,可能会出现奇怪的结果。每个操作系统对行结束的标准都不同。在Linux上,文本文件中的每一行都以一个换行符结束(ASCII 10)。在Windows上,文本行以回车(ASCII 13)结束,后面跟着一个换行符。而在OS X上,一个文本文件可能只用换行符或回车符结束每一行。如果 grep 不能正确匹配行尾,可以使用 cat -v 检查非Linux的行尾字符,它会将回车符显示为 ^M

→ cat -v dosfile.
哎呀!这个文件似乎在每行的结尾处使用了^M
在换行符之前有回车符。^M

要删除回车符,可以使用 tr -d 命令:

→ tr -d '\r' < dosfile. > linuxfile.
→ cat -v linuxfile.
哎呀!这个文件似乎在每行的结尾处使用了
在换行符之前有回车符。

fgrep

stdin  stdout  - file  -- opt  --help  --version

fgrep [选项] [固定字符串] [文件]

fgrep(fgrep) 命令就像 grep(grep),但是它接受的是由换行符分隔的固定字符串列表,而不是正则表达式。它与 grep -F 是相同的。例如,如果你有一个每行都是字符串的字典文件:

→ cat my_dictionary_file
aardvark
aback
abandon
...

你可以方便地在一组输入文件中搜索这些字符串:

→ fgrep -f my_dictionary_file story
a little aardvark who went to
visit the abbot at the abbey.

通常,你会使用小写的 -f 选项让 fgrep 从文件中读取固定字符串。你也可以使用引号在命令行中读取固定字符串,但这有点复杂。要在文件中搜索字符串“one”、“two”和“three”,你需要输入:

→ fgrep 'one       注意我们在输入换行符
two
three' myfile

当搜索*和{等非字母数字字符时,fgrep 很方便,因为它们会被当作字面值,而不是正则表达式字符。

cut

stdin  stdout  - file  -- opt  --help  --version

cut -(b|c|f)range [选项] [文件]

cut(cut) 命令从文件中提取文本的列。“列”是由字符偏移定义的(例如,每行的第19个字符):

→ cut -c19 myfile

或者由字节偏移定义(如果你的语言有多字节字符,那么字节和字符是不同的):

→ cut -b19 myfile

或者由分隔字段定义(例如,在一个逗号分隔的文件data.csv中的每一行的第五个字段):

→ cat data.csv
one,two,three,four,five,six,seven
ONE,TWO,THREE,FOUR,FIVE,SIX,SEVEN
1,2,3,4,5,6,7
→ cut -f5 -d, data.csv
five
FIVE
5

你不仅限于输出一个单列:你可以给出范围(3-16),逗号分隔序列(3,4,5,6,8,16)或两者都有(3,4,8-16)。对于范围,如果你省略了第一个数字(-16),则默认为1(1-16);如果你省略了最后一个数字(5-),则默认到行尾。

有用的选项

-d C使用字符 C 作为 -f 选项之间的 输入 分隔符,默认为制表符。
--output-delimiter=C使用字符 C 作为 -f 选项之间的 输出 分隔符,默认为制表符。
-s抑制(不打印)不包含分隔符的行。

paste

stdin  stdout  - 文件  -- 选项  --help  --version

paste [选项] [文件]

paste命令与cut命令恰好相反:它将多个文件视为垂直列并在标准输出中合并它们:

→ cat letters
A
B
C
→ cat numbers
1
2
3
4
5
→ paste numbers letters
1  A
2  B
3  C
4
5
→ paste letters numbers
A  1
B  2
C  3
   4
   5

有用的选项

-d delimiters在列之间使用给定的 delimiters 字符;默认为制表符。提供一个字符(-d:)总是使用,或者一个字符列表(-dxyz)在每一行上按顺序应用(第一个分隔符是x,然后是y,然后是z,然后是x,然后是y,...)。
-s横向:转置输出的行和列:→ **paste -s letters numbers** A B C 1 2 3 4 5

tr

stdin  stdout  - 文件  -- 选项  --help  --version

tr [选项] charset1 [charset2]

tr命令执行一些简单的、有用的字符集转换。例如,要把文件中的所有内容大写:

→ cat wonderfulfile
This is a very wonderful file.
→ cat wonderfulfile | tr 'a-z' 'A-Z'
THIS IS A VERY WONDERFUL FILE.

或者把所有元音改为星号:

→ cat wonderfulfile | tr aeiouAEIOU '*'
Th*s *s * v*ry w*nd*rf*l f*l*.

或者删除所有元音:

→ cat wonderfulfile | tr -d aeiouAEIOU
Ths s  vry wndrfl fl.

作为一个实际的例子,从DOS文本文件中删除所有回车符,使其与grep等Linux文本工具更兼容:

→ tr -d '\r' < dosfile. > linuxfile.

tr将 charset1 中的第一个字符转换为 charset2 中的第一个字符,第二个转换为第二个,第三个转换为第三个,依此类推。如果 charset1 的长度为 *N*,则只使用 charset2 中的前 N 个字符。(如果 charset1 比 charset2 长,请参阅 -t 选项。)

字符集可以有以下形式:

形式含义
ABDG字符A、B、D、G的序列。
A-Z从A到Z的字符范围。
[x*y]字符 x 的 y 重复。
[: class :]grep接受的字符类([:alnum:][:digit:]等)。

tr还理解printf接受的转义字符“\a”(^G = 响铃警告),“\b”(^H = 退格),“\f”(^L = 换页),“\n”(^J = 新行),“\r”(^M = 返回),“\t”(^I = 制表符),以及“\v”(^K = 垂直制表符),以及符号*nnn*表示八进制值为 nnn 的字符。

tr适用于快速简单的翻译,但对于更强大的任务,请考虑使用sedawkperl

有用的选项

-d从输入中删除 charset1 中的字符。
-s从输入中删除 charset1 中找到的相邻重复项。例如,tr -s aeiouAEIOU 会将相邻的重复元音压缩为单个元音(reeeeeeally 会变成 really)。
-c补码:对 charset1 中未找到的所有字符进行操作。
-t如果 charset1 比 charset2 长,通过截短 charset1 使它们长度相同。如果没有 -t,则 charset2 的最后一个字符(看不见的)会重复,直到 charset2 与 charset1 的长度相同。

expand

stdin  stdout  - file  -- opt  --help  --version

expand [options] [files]
unexpand [options] [files]

expand(展开)命令将制表符转换为看起来等效的空格字符数量,unexpand(取消展开)则执行相反的操作。默认情况下,每八个空格处就有一个制表位,但你可以通过选项更改这个设置。这两个程序默认都写入标准输出。

→ expand tabfile > spacefile
→ expand spacefile > tabfile

要检查文件中是否包含空格或制表符,使用 cat -T 命令,它将制表符显示为 ^I,或者 od -c 命令,它将制表符显示为 \t

Useful options

-t N指定每 N 个空格就有一个制表位。

sort

stdin  stdout  - file  -- opt  --help  --version

sort [options] [files]

sort(排序)命令按照字母顺序或者你指定的其他规则打印文本行。所有提供的文件都被连接起来,然后对结果进行排序并打印:

→ cat threeletters
def
xyz
abc
→ sort threeletters
abc
def
xyz

Useful options

-f忽略大小写排序。
-n数字排序(例如,9在10之前),而不是字母排序(10在9之前,因为它以“1”开头)。
-g另一种数字排序方法,使用不同的算法,该算法在其他方面认识到科学记数法(7.4e3表示“7.4乘以10的三次方”,即7400)。运行 info sort 以获取完整的技术细节。
-u唯一排序:忽略重复行。(如果与 -c 一起用于检查已排序的文件,如果有任何连续的行是相同的,就会失败。)
-c不排序,只检查输入是否已经排序。如果是,什么也不打印;否则,打印一个错误消息。
-b忽略行的开头空格。
-r反转输出:从大到小排序。
-t X将 X 用作 -k 选项的字段分隔符。
-k key选择排序键。(与 -t 结合使用,选择键之间的分隔符字符。)

排序键表示在排序时要考虑的行的一部分,而不是整行。一个例子可能是每行的第五个字符。通常,sort 会认为这些行是排序的:

aaaaz
bbbby

但是如果你的排序键是“每行的第五个字符”,表示为 -k1.5,那么行会被反转,因为 y 在 z 之前。更实用的例子包括这个包含姓名和地址的文件:

→ cat people
George Washington,123 Main Street,New York
Abraham Lincoln,54 First Avenue,San Francisco
John Adams,39 Tremont Street,Boston

一个普通的排序会首先显示“Abraham Lincoln”这行。但是如果你把每行看作三个逗号分隔的值,你可以用以下命令按第二个值进行排序:

→ sort -k2 -t, people
George Washington,123 Main Street,New York
John Adams,39 Tremont Street,Boston
Abraham Lincoln,54 First Avenue,San Francisco

其中,“123 Main Street”在字母上是第一位。同样,你可以按城市(第三个值)进行排序:

→ sort -k3 -t, people
John Adams,39 Tremont Street,Boston
George Washington,123 Main Street,New York
Abraham Lincoln,54 First Avenue,San Francisco
项目意义默认值
F1起始字段必须提供
C1起始字段1内的起始位置1
F2结束字段最后一个字段
C2结束字段内的起始位置1

因此,sort -k1.5基于第一个字段进行排序,从第五个字符开始;而sort -k2.8,5意味着“从第二个字段的第八个字符开始,到第五个字段的第一个字符结束。”-t选项改变了-k的行为,所以它会考虑到像逗号这样的分隔符字符,而不是空格。

你可以重复-k选项来定义多个键,这些键将按照命令行中找到的顺序从第一个到最后一个应用。

uniq

stdin  stdout  - file  -- opt  --help  --version

uniq [options] [files]

uniq(唯一)命令对文本的连续重复行进行操作。例如,如果你有一个文件 myfile

→ cat letters2
a
b
b
c
b

那么uniq会检测并处理两个连续的b,但不会处理第三个b:

→ uniq letters2
a
b
c
b

uniq经常在对文件进行排序后使用:

→ sort letters2 | uniq
a
b
c

在这种情况下,只剩下一个b,因为所有三个b都通过sort变成了相邻的,然后通过uniq压缩成一个。另外,你也可以统计重复行的数量,而不是消除它们:

→ sort letters2 | uniq -c
      1 a
      3 b
      1 c

有用的选项

-c计数相邻重复行。
-i大小写不敏感的操作。
-u只打印唯一的行。
-d只打印重复的行。
-s N在检测重复时,跳过每行的前*N*个字符。
-f N在检测重复时,忽略每行前*N*个以空格分隔的字段。
-w N在检测重复时,只考虑每行的前*N个字符。如果与-s-f一起使用,sort将首先忽略指定数量的字符或字段,然后考虑接下来的N*个字符。

tee

stdin  stdout  - file  -- opt  --help  --version

tee [options] files

cat命令一样,tee(茶)命令将标准输入复制到标准输出,不进行任何更改。同时,它也将相同的标准输入复制到一个或多个文件。tee通常出现在管道的中间,将一些中间数据写入文件,同时也将其传递给管道中的下一个命令:

→ who | tee original_who | sort
barrett   pts/1    Sep 22 21:15
byrnes    pts/0    Sep 15 13:51
silver       :0    Sep 23 20:44
silver    pts/2    Sep 22 21:18

这个命令行在屏幕上产生who的排序输出,但同时也将who的原始,未排序的输出写入到文件 original_who

→ cat original_who
silver       :0    Sep 23 20:44
byrnes    pts/0    Sep 15 13:51
barrett   pts/1    Sep 22 21:15
silver    pts/2    Sep 22 21:18

然后将同样的输出传递到管道的其余部分(sort),在屏幕上产生排序的输出。

有用的选项

-a附加,而不是覆盖文件。
-i忽略中断信号。

更强大的操作

我们仅仅接触了Linux文本过滤的冰山一角。Linux拥有数百种过滤器,能够实现对数据的更复杂的处理。但是,强大的功能往往意味着陡峭的学习曲线,这对于一篇文章来说太多了。以下是一些可以帮助你入门的过滤器。

awk

AWK是一种基于模式匹配的语言。它通过正则表达式匹配数据,然后根据数据执行操作。以下是一些处理文本文件myfile的简单示例。

打印每行的第二个和第四个单词:

→ awk '{print $2, $4}' myfile

打印所有字符数少于60个的行:

→ awk 'length < 60 {print}' myfile

sed

就像AWK一样,sed也是一个模式匹配引擎,可以对文本行进行操作。它的语法与vim和行编辑器ed密切相关。以下是一些简单的示例。

打印文件,将所有出现的字符串“me”更改为“YOU”:

→ sed 's/me/YOU/g' myfile

打印文件,去掉前10行:

→ sed '1,10d' myfile

m4

m4是一种宏处理语言和命令。它在文件中查找关键字,并为它们替换值。例如,给定此文件:

→ cat substitutions
My name is NAME and I am AGE years old.
ifelse(QUOTE,yes,Learn Linux today!)

看看m4如何对NAMEAGEQUOTE进行替换:

→ m4 -DNAME=Sandy substitutions
My name is Sandy and I am AGE years old.
→ m4 -DNAME=Sandy -DAGE=25 substitutions
My name is Sandy and I am 25 years old.

→ m4 -DNAME=Sandy -DAGE=25 -DQUOTE=yes substitutions
My name is Sandy and I am 25 years old.
Learn Linux today!

推荐阅读

··································

你好,我是拾叁,7年开发老司机、互联网两年外企5年。怼得过阿三老美,也被PR comments搞崩溃过。这些年我打过工,创过业,接过私活,也混过upwork。赚过钱也亏过钱。一路过来,给我最深的感受就是不管学什么,一定要不断学习。只要你能坚持下来,就很容易实现弯道超车!所以,不要问我现在干什么是否来得及。如果你还没什么方向,可以先关注我,这里会经常分享一些前沿资讯和编程知识,帮你积累弯道超车的资本。


您可能也对以下帖子感兴趣

文章有问题?点此查看未经处理的缓存